package org.example;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @author xuan
 * @create 2024/4/22
 */
@ResponseBody
@Controller
public class RedisController {
    @Resource
    private RedisService redisService;
    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @GetMapping("/set/{key}/{value}")
    public String set(@PathVariable String key, @PathVariable String value) {
        redisService.set(key, value);
        return "Set key:" + key + ", value: " + value;
    }

    @RequestMapping(method = RequestMethod.GET, path = "/set")
    public String set2(String name) {
        redisService.set("name", name);
        return "Set key: name, " + "value: " + name;
    }

    @GetMapping("/get/{key}")
    public Object get(@PathVariable String key) {
        return redisService.get(key);
    }

    @RequestMapping(method = RequestMethod.GET, path = "/get")
    public Object get2(String key) {
        return redisService.get(key);
    }

    @GetMapping("/delete/{key}")
    public String delete(@PathVariable String key) {
        redisService.delete(key);
        return "Delete key: " + key;
    }

    @RequestMapping(method = RequestMethod.GET, path = "/delete")
    public String delete2(String key) {
        redisService.delete(key);
        return "Delete key: " + key;
    }

    @GetMapping("/setWithTime")
    public String setWithTime(String key, String value, long time) {
        redisService.set(key, value, time, TimeUnit.MILLISECONDS);
        return "Set key: " + key + ",value: " + value + "continueTime: " + time;
    }

    @GetMapping("/test")
    public String test() {
        boolean equals1 = Boolean.TRUE.equals(redisTemplate.execute((RedisCallback<Boolean>) connection -> connection.bitCount("user:country:bind".getBytes()) > 0));
        System.out.println(equals1);

        redisTemplate.opsForValue().setBit("user:country:bind",168,true);
        boolean equals2 = Boolean.TRUE.equals(redisTemplate.execute((RedisCallback<Boolean>) connection -> connection.bitCount("user:country:bind".getBytes()) > 0));
        System.out.println(equals2);

        boolean equals3 = Boolean.TRUE.equals(redisTemplate.opsForValue().getBit("user:country:bind", 169));
        System.out.println(equals3);
        return "equals1:" + equals1 + "&equals2:" + equals2;
    }

    @PostMapping("/testRedisBitCache")
    public void testRedisBitCache() {
        List<String> userIds = new ArrayList<>();
        userIds.add("260");
        userIds.add("360");
        userIds.add("460");

        for (String userId : userIds) {
            //redis中的bit位图有什么作用呢？你可以想想现在有一个需求，你们有个系统，现在系统里可以拉新新用户，你每拉新一个别的用户注册你们的系统之后，你和被你拉新的那个新用户
            //都可以获取200积分，最终这些积分可以在你们系统里面进行礼品兑换。那么怎么标记你拉新了多少人呢？在数据库层面肯定是需要加一个数据库表，你的id要映射多个被你拉的用户的id；
            //那么在缓存redis层面该如何处理呢？就可以使用位图，这个键recruit_user_168指定了业务需求recruit_user为拉新用户，然后拉别的用户的那个人的用户主键id是168
            //然后有了这个键之后，我们可以为这个键创造一个位图，什么是位图呢？首先你需要先理解计算机存储数据是通过2进制存储的，一个int类型的数字就包含了4个字节，32位；
            //而所谓的setBit位图缓存，就是从0位还是，往后便宜多个位，然后把这个位上的二进制数字从0设置为1；
            //对于redis的bit位图计算，一个键，比如这里的recruit_user_168它的位有好几亿个，所以量是很大的；所以你基本上不用担心一个键的位图会用完
            //假如现在你想要标记168这个用户拉新了多少个新用户，你就可以使用位图运算，首先key键设置为recruit_user_168即可，然后每拉取一个新用户就像下面这句代码这样把二进制位从0设置为1
            redisTemplate.opsForValue().setBit("recruit_user_168", Long.parseLong(userId), true);
        }

        //todo 注：这里虽然每次都是写的recruit_user_168这个键，但是序列化的结果是不一样的，这就会导致在执行下面的connection.bitCount("user:country:bind".getBytes())获取
        //todo 的位图对应的键并不是同一个，所以会导致位图数据异常。那么怎么解决呢？我们写一个RedisConfig配置类，在里面给redisTemplate进行一个序列化配置即可。详情请参照RedisConfig类
        //那假如，在用户拉新中，针对于用户168，我们想要看一下他已经拉新的用户个数有多少怎么看？我们只需要查看一下这个用户的键对应的位图中标识为1的二进制位的个数即可
        Long count = redisTemplate.execute((RedisCallback<Long>) connection -> {
            return connection.bitCount("recruit_user_168".getBytes());
        });
        System.out.println("键recruit_user_168对应的位图中，表示为1的位一共有：" + count + "个");

        boolean result = redisTemplate.execute((RedisCallback<Boolean>) connection ->
                        connection.bitCount("recruit_user_168".getBytes()) > 0
        );
        System.out.println(result);

        for (String userId : userIds) {
            System.out.println("当前userId:" + userId + "  " +redisTemplate.opsForValue().getBit("recruit_user_168", Long.parseLong(userId)));
        }

        //todo 利用redis的bit位图，我们还可以处理类似于一个月30天的用户签到问题，逻辑和上面的一个用户拉新了多少位别的用户的逻辑也是类似的；不过上面的逻辑中有个缺陷，就是你只能知道一个用户
        //todo 拉新了多少位别的用户，但是他拉新的这些用户的具体的用户id你并不能获取到；所以redis的bit位图逻辑其实还是更适合处理类似于处理一个月30天有多少天签到的问题

    }
}


































